﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using FinPlusAssembler;
using FinPlusInterfaces;

namespace FinPlusCompCore
{
    public class FinPlusSyncThrottle:FinPlusComponent
    {
        [State(Test = true)]
        public override bool IsValid { get; set; }
        [State(Test = true)]
        public override string TestString { get { return Adapter.ToString(); } }
        public override IPods Adapter { get { return _adapter; } }
     
        private enum Params{ Refresh, Resend };
        private readonly IThrottle _throttle;
        private readonly IFinPlusComp _sourceConn;
        private readonly IPods _adapter;
        private readonly IDictionary<string, string> _throttleBeans;
        private readonly IList<string> _syncBeans;
        private readonly bool _withResend;

        //construct
        public FinPlusSyncThrottle(IFinPlusComp source, string[] syncBeans, IDictionary<string, string> throttleBeans, int throttle, bool withResend)
        {
            _sourceConn = source;

            _syncBeans = syncBeans;
            _throttleBeans = throttleBeans;
            _withResend = withResend;
            _throttle = IOC.New<IThrottle>(throttle, true, _withResend);

            _throttle.Start(Update);
            foreach (var throttleBean in _throttleBeans)
                _sourceConn.Adapter.Get(throttleBean.Key, IOC.New<IPod>(), Bean_Updated);

            _adapter = _sourceConn.Adapter.Clone();
            _adapter.Get(Params.Resend.ToString(), string.Empty, Bean_Resend);
            _adapter.Get(Params.Refresh.ToString(), string.Empty);

            _throttle.IsBusy = false;
            IsValid = true;
        }

        //common control interface
        public override void Dispose()
        {
            if (_sourceConn != null)
                _sourceConn.Dispose();
        }

        //private 
        private void Update(IDictionary<string, object> updates)
        {
            try
            {
                var pods = UpdateAdapter(updates);               
                var syncs = SyncAdapter(pods);

                //TODO reponse resync not finished

                _throttle.IsBusy = false;
            }
            catch (Exception e)
            {
                _throttle.IsBusy = IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private IDictionary<int, IPod> UpdateAdapter(IDictionary<string, object> updates)
        {
            var pods = new Dictionary<int, IPod>();
            foreach (IBean update in updates.Values)
            {
                if (!_adapter.ContainsKey(update.Id)) 
                    continue;
                
                var pod = _adapter[update.Id];
                var bean = pod.Get(update.Name);
                
                if (bean == null)
                    continue;

                bean.Update(update.ToString());
                pods[pod.Id] = pod;
            }

            return pods;
        }

        private IList<string> SyncAdapter(IDictionary<int, IPod> pods)
        {
            var syncs = new List<string>();

            foreach (var syncBean in _syncBeans)
            {
                foreach (var pod in pods.Values)
                {
                    var bean = pod.Get(syncBean);
                    if (bean == null || string.IsNullOrEmpty(bean.ToString())) 
                        continue;

                    foreach (var value in bean.ToString().Split(','))
                    {
                        if (syncs.Contains(value)) 
                            continue;

                        pod.Get(Params.Refresh.ToString()).Update(value);
                        syncs.Add(value);
                    }
                }
            }

            return syncs;
        }

        //events
        private void Bean_Updated(object s, PropertyChangedEventArgs a)
        {
            var bean = (IBean)s;

            if (!_throttleBeans.ContainsKey(bean.Name)) 
                return;

             var clone = bean.Clone(bean.Id);
             _throttle.Put(Key(clone, _throttleBeans[bean.Name]), clone, clone.TimeStamp);
        }

        private void Bean_Resend(object s, PropertyChangedEventArgs a)
        {
            var bean = (IBean)s;
            var key = string.Format("{0}|{1}|", bean.Id, bean.ToString());
            _throttle.Resend(key);
        }

        private string Key(IBean bean, string key = null)
        {
            var k = string.Format("{0}|{1}|", Id, bean.Name);
            if (k != null && bean.Type.Equals(BeanType.Pod.ToString()))
                k += ((IPod)bean.Object)[key];

            return k;
        }
    }
}
